package time.providers;
import time.providers.base.Driver;

/**
 * This class is a placeholder for providers, It is made for advanced uses of Time that would need many
 * different providers. A classic example of this are sessions backed by memcached, while the whole application logic
 * is powered by mysql. Please to be noted that Provider MUST be static variables, and all systems that
 * need it will only accept a static variable to be passed.
 * 
 * @author waneck
 */

class Provider 
{
	#if macro
	//this is the container that will bridge the compile-time specification of providers, and the run-time statics setup
	static var providers:Hash<Provider> = new Hash();
	#end
	
	static var aliases:Hash<Array<Provider>> = new Hash();
	
	public var sourcePath(default,null):String;
	public var driver(default, null):Driver;
	private var extensions:Array<String>;
   	#if macro public #else private #end function new(driver:Driver, ?extensions:Array<Class<Dynamic>>) 
	{
		this.driver = driver;
		this.extensions = [];
		
		//We won't take references of this. We only need to get the class to compile at this state, and
		//run the initialization code if there is any.
		if (null != extensions)
			for (ext in extensions)
			{
				if (Reflect.hasField(ext, "initExtension"))
					untyped ext.initExtension(this);
				
				this.extensions.push(Type.getClassName(ext));
			}
		
		this.extensions.sort(Reflect.compare);
	}
	
	#if macro //why only macro? because this hack is only needed so we can get the static names that reference the Provider
	
	public static function set(path:String, p:Provider, metas:Array<String>, ?alias:String) : Void
	{
		var spath = p.sourcePath;
		if (null == spath)
			p.sourcePath = path;
		//haxe.macro.Context.getType(path.substr(0,path.lastIndexOf("."))); //ensure type is on runtime as well;
		
		providers.set(path, p);
		if (null != alias)
		{
			var arr = aliases.get(alias);
			if (null == arr)
			{
				arr = [];
				aliases.set(alias, arr);
			}
			
			arr.push(p);
		}
	}
	
	public function getDriver() : Driver
	{
		return driver;
	}
	
	#else
	
	public function objectStore() : time.runtime.ObjectStore
	{
		return driver;
	}
	
	#end
	
	public function getLikeness()
	{
		return Type.getClassName(Type.getClass(this.driver)) + "::" + this.extensions.toString();
	}
	
	public function getExtensions()
	{
		return this.extensions.concat(driver.extensions());
	}
	
	public static function getFromAlias(alias:String) : Array<Provider>
	{
		var arr = aliases.get(alias);
		if (null == arr)
			return [];
		return arr;
	}
	
	
	//TODO put here all necessary provider querying methods,
	//like how it handles references, the acid compliance level, etc
	
	/**
	 *  Returns true if the driver supports references on queries.
	 */
	public function supportsReferences() : Bool
	{
		return driver.supportsReferences();
	}
	
}